#    Tichy
#
#    copyright 2008 Guillaume Chereau (charlie137@gmail.com)
#
#    This file is part of Tichy.
#
#    Tichy is free software: you can redistribute it and/or modify it
#    under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    Tichy is distributed in the hope that it will be useful, but
#    WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#    General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with Tichy.  If not, see <http://www.gnu.org/licenses/>.

__docformat__ = 'reStructuredText'

from tichy.object import Object
from tichy.item import Item
import tichy


class Frame(object):
    """Define a frame from an image"""

    def __init__(self, image):
        self.image = image

    def __repr__(self):
        return "Frame(%s)" % self.image

    def draw(self, painter, size):
        return painter.draw_frame(self, size)


class Color(object):

    def __init__(self, r, g, b):
        self.rgb = r, g, b

    def __repr__(self):
        return "Color(%d, %d, %d)" % self.rgb

    def draw(self, painter, size):
        return painter.fill(self.rgb, size)


class Font(object):

    def __init__(self, file_=None, size=24):
        self.file = file_
        self._size = size
        self.font = None

    def load(self):
        if not self.font:
            self.font = tichy.gui.Font(self.file, self._size)

    def render(self, painter, text, color):
        self.load()
        return self.font.render(text, True, color)

    def resize(self, size):
        """Create a new font identical to this one but with a different size
        """
        return Font(self.file, size)

    def size(self, line):
        self.load()
        return self.font.size(line)

    def get_linesize(self):
        self.load()
        return self.font.get_linesize()


class Condition(object):

    def __call__(self, widget):
        raise NotImplementedError


class All(Condition):

    def __call__(self, widget):
        return True

    def __repr__(self):
        return "All"


class WidgetType(Condition):

    def __init__(self, type_):
        from tichy.gui import Widget
        assert issubclass(type_, Widget)
        self.type = type_

    def __call__(self, widget):
        return isinstance(widget, self.type)

    def __repr__(self):
        return repr(self.type)


class Tag(Condition):

    def __init__(self, value):
        assert isinstance(value, str)
        self.value = value

    def __call__(self, widget):
        return self.value in widget.tags

    def __repr__(self):
        return "Tag(%s)" % self.value


class Child(Condition):

    def __init__(self, condition):
        assert isinstance(condition, Condition)
        self.condition = condition

    def __call__(self, widget):
        parent = widget.parent
        while parent is not None:
            if self.condition(parent):
                return True
            parent = parent.parent
        return False


STYLE_ATTRIBUTES = ['background', 'min_size', 'font', 'spacing', 'border',
                    'optimal_size', 'font_color']


class Style(Item):

    def __init__(self, **args):
        super(Style, self).__init__()
        assert all(x in STYLE_ATTRIBUTES for x in args.keys())
        for attr in STYLE_ATTRIBUTES:
            setattr(self, attr, args.get(attr, None))

    def __repr__(self):
        ret = {}
        for attr in STYLE_ATTRIBUTES:
            value = getattr(self, attr, None)
            if value:
                ret[attr] = value
        return repr(ret)

    def _apply_into(self, widget, style):
        for attr in STYLE_ATTRIBUTES:
            value = getattr(self, attr, None)
            if value is not None:
                setattr(style, attr, value)


class StyleSheet(Item):

    def __init__(self, *elements):
        super(StyleSheet, self).__init__()
        self.elements = elements
        assert all(isinstance(x[0], Condition) for x in self.elements)
        assert all(isinstance(x[1], Style) for x in self.elements)

    def apply(self, widget):
        ret = Style()
        self._apply_into(widget, ret)
        return ret

    def _apply_into(self, widget, style):
        for condition, cond_style in self.elements:
            if condition(widget):
                cond_style._apply_into(widget, style)

    def merge(self, style):
        """
        Merge two StyleSheet

        :Parameters:
            style : StyleSheet | Style

        :Return: StyleSheet
        """
        if isinstance(style, Style):
            return self.merge(StyleSheet((All(), style)))
        assert isinstance(style, StyleSheet)
        return StyleSheet(*(self.elements + style.elements))

    # All the parsing methods, that allow us to write stylesheet in yaml file #

    @classmethod
    def _style_from_dict(cls, dict_):
        attrs = {}
        for key, value in dict_.items():
            if isinstance(value, list):
                if len(value) == 2:
                    from tichy.gui import Vect
                    value = Vect(*value)
                elif len(value) == 3:
                    pass
                else:
                    raise ValueError
            if isinstance(value, str):
                if value.startswith('Font(') and value.endswith(')'):
                    value = value[5:-1]
                    args = [eval(x) for x in value.split(',')]
                    value = Font(*args)
                elif value.startswith('Color(') and value.endswith(')'):
                    value = value[6:-1]
                    args = [eval(x) for x in value.split(',')]
                    value = Color(*args)
                elif value.startswith('Frame(') and value.endswith(')'):
                    value = value[6:-1]
                    arg = eval(value)
                    from tichy.image import Image
                    value = Frame(Image(cls.path(arg)))
                elif value.startswith('Image(') and value.endswith(')'):
                    from tichy.image import Image
                    arg = eval(value[6:-1])
                    value = Image(cls.path(arg))
                else:
                    raise ValueError("bad attribute : %s" % value)
            attrs[key] = value
        return Style(**attrs)

    @classmethod
    def _elements_from_struct(cls, struct):
        assert isinstance(struct, list)
        elements = []
        for element in struct:
            assert isinstance(element, dict)
            condition = cls._condition_from_string(element.keys()[0])
            style = cls._style_from_dict(element.values()[0])
            elements.append((condition, style))
        return elements

    @classmethod
    def _elements_from_yaml(cls, file_):
        import yaml
        struct = yaml.safe_load(file_)
        return cls._elements_from_struct(struct)

    @classmethod
    def _condition_from_string(cls, string):
        if string == 'All':
            return All()
        if string.startswith('Tag(') and string.endswith(')'):
            return Tag(string[4:-1])
        import tichy.gui
        if hasattr(tichy.gui, string):
            widget_cls = getattr(tichy.gui, string)
            assert issubclass(widget_cls, tichy.gui.Widget)
            return WidgetType(widget_cls)
        raise ValueError("bad style condition : %s" % string)

    @classmethod
    def from_yaml(cls, yaml):
        elements = cls._elements_from_yaml(yaml)
        return cls(*elements)
